/**
 * 
 */
package soa.security.dao.impl;

import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.naming.Name;
import javax.naming.NamingException;

import org.apache.commons.lang3.StringUtils;
import org.ldaptive.Connection;
import org.ldaptive.LdapEntry;
import org.ldaptive.LdapException;
import org.ldaptive.SearchOperation;
import org.ldaptive.SearchRequest;
import org.ldaptive.SearchResult;
import org.ldaptive.SortBehavior;
import org.ldaptive.io.LdifWriter;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.query.ContainerCriteria;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.query.SearchScope;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.stereotype.Repository;

import soa.security.Constants;
import soa.security.dao.OrganizationDao;
import soa.security.domain.Organization;

/**
 * @author Cre.Gu
 *
 */
@Repository
public class OrganizationDaoImpl extends SpringLdapDaoImpl<Organization>
		implements OrganizationDao {

	@Override
	public void create(Organization t) {
		t.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
				.format(new Date()));

		ldapTemplate.create(t);
	}

	@Override
	public boolean exist(String organizationId) {
		return findDnById(organizationId) != null;
	}

	@Override
	public Organization findByPrimaryKey(String organizationId) {
		if (exist(organizationId))
			return findByDn(findDnById(organizationId).toString());

		return null;
	}

	@Override
	public Name findDnById(String id) {
		try {
			LdapQuery query = LdapQueryBuilder.query().where("objectclass")
					.is("zjtzsw-organization").and("bid").is(id);

			DirContextAdapter context = (DirContextAdapter) ldapTemplate
					.searchForContext(query);

			return context.getDn();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public Organization findByDn(String dn) {
		Organization or = findOrganizationByDn(dn);
		if(StringUtils.isNotBlank(or.getParentBid()))
			or.setParent(findByPrimaryKey(or.getParentBid()));
		return or;
	}

	public List<String> allAttrName(Name dn, String attrName) {
		return allAttrName(dn, attrName);
	}

	@Override
	public List<Organization> allSubOrganizations(Organization or,
			Map<String, Object> parameters) {
		/*
		 * LdapQuery query = LdapQueryBuilder.query().base(or.getDn())
		 * .searchScope(SearchScope.ONELEVEL).where("objectclass")
		 * .is("zjtzsw-organization").and(Constants.DELETED).not() .is("TRUE");
		 */

		ContainerCriteria query = baseQuery(or.getDn().toString(),
				SearchScope.ONELEVEL);

		buildLikeCriteria(query, parameters, "name");
		buildLikeCriteria(query, parameters, "organizationCode");
		buildEqualsCriteria(query, parameters, "organizationType");

		return ldapTemplate.search(query, new OrganizationContextMapper());
	}

	@Override
	public Organization findByPrimaryKey(String baseDn, String id) {
		return findByPrimaryKey(id);
	}

	@Override
	public String backupData(String ldapBase) {
		StringWriter writer = new StringWriter();
		LdifWriter ldifWriter = new LdifWriter(writer);
		Connection conn = null;
		try {
			conn = pool.getConnection();
			conn.open();

			SearchOperation search = new SearchOperation(conn);
			find(search, LdapUtils.newLdapName(ldapBase).toString(), ldifWriter);

			return writer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			conn.close();
		}
		return null;
	}

	private void find(SearchOperation search, String baseDn,
			LdifWriter ldifWriter) throws LdapException, IOException {
		SearchRequest request = new SearchRequest(baseDn, "(ObjectClass=*)");
		request.setSearchScope(org.ldaptive.SearchScope.ONELEVEL);
		request.setSortBehavior(SortBehavior.SORTED);

		SearchResult result = search.execute(request).getResult();
		Iterator<LdapEntry> iter = result.getEntries().iterator();
		ldifWriter.write(result);
		System.out.println(baseDn + ": " + result.getEntries().size());
		while (iter.hasNext()) {
			LdapEntry entry = iter.next();
			find(search, entry.getDn(), ldifWriter);
		}
	}

	@Override
	public List<Organization> allOrganizations(String baseDn,
			org.springframework.ldap.query.SearchScope searchScope,
			Map<String, Object> parameters) {
		ContainerCriteria query = baseQuery(baseDn, searchScope);

		buildLikeCriteria(query, parameters, "name");
		buildLikeCriteria(query, parameters, "organizationCode");
		buildEqualsCriteria(query, parameters, "organizationType");

		return ldapTemplate.search(query, new OrganizationContextMapper());
	}

	@Override
	public String getObjectClass() {
		return "zjtzsw-organization";
	}

	protected class OrganizationContextMapper implements
			ContextMapper<Organization> {
		@Override
		public Organization mapFromContext(Object ctx) throws NamingException {
			DirContextAdapter context = (DirContextAdapter) ctx;
			return findByDn(context.getDn().toString());
		}
	}

	@Override
	public void deleteByDn(String dn) {
		ldapTemplate.unbind(dn);
	}
	
	@Override
	public List<Organization> existCode(String code) {
		// TODO Auto-generated method stub
		LdapQueryBuilder queryBuilder = LdapQueryBuilder.query();
		LdapQuery query = queryBuilder.searchScope(SearchScope.SUBTREE)
				.where("objectclass").is("zjtzsw-organization")
				.and(Constants.DELETED).not().is("TRUE").and("ccode").is(code);

		List<Organization> org = ldapTemplate.search(query, new OrganizationContextMapper());
		return org;
	}

	@Override
	public List<Organization> existOrganizationCode(String organizationCode) {
		// TODO Auto-generated method stub
		LdapQueryBuilder queryBuilder = LdapQueryBuilder.query();
		LdapQuery query = queryBuilder.searchScope(SearchScope.SUBTREE)
				.where("objectclass").is("zjtzsw-organization")
				.and(Constants.DELETED).not().is("TRUE").and("organizationCode").is(organizationCode);

		List<Organization> org = ldapTemplate.search(query, new OrganizationContextMapper());
		return org;
	}
}
